use std::collections::HashMap;
use std::hash::BuildHasherDefault;

use twox_hash::XxHash64;

use crate::gen_title;

pub fn ch2_22_1() {
    gen_title("ch2_22_1", 18);

    let mut my_games = HashMap::new();

    my_games.insert("红宝石", 1);
    my_games.insert("蓝宝石", 2);
    my_games.insert("河边剪的误以为是宝石的破石头", 3);

    println!("my_games: {:?}", my_games);
}

pub fn ch2_22_2() {
    gen_title("ch2_22_2", 18);

    let teams_list = vec![
        ("中国队".to_string(), 100),
        ("美国队".to_string(), 10),
        ("日本队".to_string(), 50),
    ];

    let mut teams_map = HashMap::new();
    for team in &teams_list {
        teams_map.insert(&team.0, team.1);
    }

    println!("teams_map: {:?}", teams_map);
}

pub fn ch2_22_3() {
    gen_title("ch2_22_3", 18);

    let teams_list = vec![
        ("中国队".to_string(), 100),
        ("美国队".to_string(), 10),
        ("日本队".to_string(), 50),
    ];

    let teams_map: HashMap<String, i32> = teams_list.into_iter().collect();

    println!("teams_map: {:?}", teams_map);
}

pub fn ch2_22_4() {
    gen_title("ch2_22_4", 18);

    let name = String::from("Sunface");
    let age = 18;

    let mut handsome_boys = HashMap::new();
    handsome_boys.insert(name, age);

    println!("age: {}", age);
}

pub fn ch2_22_5() {
    gen_title("ch2_22_5", 18);

    let name = String::from("Sunface");
    let age = 18;

    let mut handsome_boys = HashMap::new();
    handsome_boys.insert(&name, age);

    println!("age: {}", age);
    println!("handsome_boys: {:?}", handsome_boys);
}

pub fn ch2_22_6() {
    gen_title("ch2_22_6", 18);

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    let team_name = String::from("Blue");
    let score: Option<&i32> = scores.get(&team_name);

    println!("score: {:?}", score);

    let score: i32 = scores.get(&team_name).copied().unwrap_or(0);
    println!("score: {}", score);
}

pub fn ch2_22_7() {
    gen_title("ch2_22_7", 18);

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }

    for (key, value) in scores {
        println!("{}: {}", key, value);
    }
}

pub fn ch2_22_8() {
    gen_title("ch2_22_8", 18);

    let mut scores = HashMap::new();

    scores.insert("Blue", 10);

    let old = scores.insert("Blue", 20);
    assert_eq!(old, Some(10));
    println!("scores: {:?}", scores);

    let new = scores.get("Blue").copied();
    assert_eq!(new, Some(20));
    println!("scores: {:?}", scores);

    let v = scores.entry("Yellow").or_insert(5);
    assert_eq!(*v, 5);
    println!("scores: {:?}", scores);

    let v = scores.entry("Yellow").or_insert(50);
    assert_eq!(*v, 5);
    println!("scores: {:?}", scores);
}

pub fn ch2_22_9() {
    gen_title("ch2_22_9", 18);

    let text = "hello world wonderful world";

    let mut map = HashMap::new();

    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }

    println!("map: {:?}", map);
}

pub fn ch2_22_10() {
    gen_title("ch2_22_10", 18);

    let mut hash: HashMap<_, _, BuildHasherDefault<XxHash64>> = Default::default();
    hash.insert(42, "the answer");
    assert_eq!(hash.get(&42).copied(), Some("the answer"));
    println!("hash: {:?}", hash);
}

pub fn ch2_22_pra_1() {
    gen_title("ch2_22_pra_1", 18);

    let mut scores: HashMap<&str, i32> = HashMap::new();
    scores.insert("Sunface", 98);
    scores.insert("Daniel", 95);
    scores.insert("Ashley", 69);
    scores.insert("Katie", 58);

    let score = scores.get("Sunface").copied();
    assert_eq!(score, Some(98));
    println!("scores: {:?}", scores);

    if scores.contains_key("Daniel") {
        let score = scores["Daniel"];
        assert_eq!(score, 95);
        scores.remove("Daniel");
        println!("scores: {:?}", scores);
    }

    assert_eq!(scores.len(), 3);

    for (name, score) in scores {
        println!("{}'s score is {}", name, score);
    }
}

pub fn ch2_22_pra_2() {
    gen_title("ch2_22_pra_2", 18);
    let teams = [
        ("Chinese Team", 100),
        ("American Team", 10),
        ("France Team", 50),
    ];

    let mut teams_map1 = HashMap::new();
    for team in teams {
        teams_map1.insert(team.0, team.1);
    }

    // 由于都是可以Clone的所以所有权未被转移
    let teams_map2 = teams.into_iter().collect();

    assert_eq!(teams_map1, teams_map2);

    println!("teams_map1: {:?}", teams_map1);
    println!("teams_map2: {:?}", teams_map2);
    println!("teams: {:?}", teams);
}

pub fn ch2_22_pra_3() {
    gen_title("ch2_22_pra_3", 18);

    let mut player_stats = HashMap::new();

    player_stats.entry("health").or_insert(100);
    assert_eq!(player_stats["health"], 100);

    player_stats
        .entry("health")
        .or_insert_with(random_stat_buff);
    assert_eq!(player_stats["health"], 100);

    let health = player_stats.entry("health").or_insert(50);
    assert_eq!(health, &100);
    *health -= 50;
    assert_eq!(*health, 50);

    println!("player_stats: {:?}", player_stats);

    fn random_stat_buff() -> u8 {
        42
    }
}

pub fn ch2_22_pra_4() {
    gen_title("ch2_22_pra_4", 18);

    #[derive(Eq, PartialEq, Hash, Debug)]
    struct Viking {
        name: String,
        country: String,
    }

    impl Viking {
        fn new(name: &str, country: &str) -> Viking {
            Viking {
                name: name.to_string(),
                country: country.to_string(),
            }
        }
    }

    let vikings = HashMap::from([
        (Viking::new("Einar", "Norway"), 25),
        (Viking::new("Olaf", "Denmark"), 24),
        (Viking::new("Harald", "Iceland"), 12),
    ]);

    for (viking, health) in &vikings {
        println!("{} {} has {} hp", viking.name, viking.country, health);
    }

    println!("vikings: {:?}", vikings);
}

pub fn ch2_22_pra_5() {
    gen_title("ch2_22_pra_5", 18);

    let mut map: HashMap<i32, i32> = HashMap::with_capacity(100);
    map.insert(1, 2);
    map.insert(3, 4);

    assert!(map.capacity() >= 100);
    println!("len: {}, capacity: {}", map.len(), map.capacity());

    map.shrink_to(50);
    assert!(map.capacity() >= 50);
    println!("len: {}, capacity: {}", map.len(), map.capacity());

    map.shrink_to_fit();
    assert!(map.capacity() >= 2);
    println!("map: {:?}", map);
    println!("len: {}, capacity: {}", map.len(), map.capacity());
}

pub fn ch2_22_pra_6() {
    gen_title("ch2_22_pra_6", 18);

    let v1 = 10;
    let mut m1 = HashMap::new();
    m1.insert(v1, v1);
    println!("m1: {:?}", m1);
    println!("v1 is still usable after inserting to hashmap : {}", v1);

    let v2 = "hello".to_string();
    let mut m2 = HashMap::new();
    m2.insert(&v2, v1);
    assert_eq!(v2, "hello");
    println!("m2: {:?}", m2);
}
